<?php

namespace App\Http\Controllers;


use App\Models\ArticleBehavior;
use App\Models\ArticleCollect;
use App\Models\ArticleComment;
use App\Models\Dispatch;
use App\Models\Event;
use App\Models\Fangtan;
use App\Models\FangtanUser;
use App\Models\Fresh;
use App\Models\FreshTopic;
use App\Models\FreshUser;
use App\Models\FreshVoice;
use App\Models\Need;
use App\Models\News;
use App\Models\NewsDesc;
use App\Models\SiteIp;
use App\Models\Topic;
use App\Models\User;
use App\Models\UserShouting;
use App\Service\BaiduSMS;
use App\Service\Ip;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Log\Logger;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Config;
use App\Service\BaiduCensor;
use Illuminate\Support\Facades\Log;

class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;

    // 翻页等
    // $page $rows 查询的时候最好重新在获取一遍
    protected $rows;
    protected $page;
    protected $isPage;
    protected $id;


    protected $controller;
    protected $action;

    protected $data = [];
    protected $_page_token;

    /**
     * Request实例
     * @var \Illuminate\Http\Request $request
     */
    protected $request;

    /**
     * 统一初始化 方便以后的统一操作
     *
     * @param \Illuminate\Http\Request $request
     * @return \Illuminate\Http\Response
     */
    public function __construct(Request $request)
    {
        // 配置信息
        $this->request = $request;
        define('LARAVEL_TIME', time());
        define('SUCCESS_CODE', 200);
        define('ERROR_CODE', 400);  // 失败时返回
        define('LOGIN_ERROR', 100); // 需要重新登录的

        // TODO 暂时没有加密解密，只有一般操作
        // 不加密：必须传递 参数 U4TIRP87hGoJhcyM，且值为mZwTzXgOawVV1GJJ
        //$request_is_encode = $request->header('U4TIRP87hGoJhcyM');
        $request_is_encode = 'mZwTzXgOawVV1GJJ'; // 默认没有加密， 需要加密则注释


        // 如果传递参数不对或者没有传递该参数，则表示传递的参数是加密的，需要解密
        if($request_is_encode != 'mZwTzXgOawVV1GJJ'){
            define('IS_ENCODE', 1); // 设置为加密状态
            // 将传递过来的参数，进行解密操作
            $contents1 = $request->all();
            $contents2 = $request->input('contents');
            $contents2 = json_decode($this->decode($contents2), true);
            $contents  = empty($contents2) ? $contents1 : array_merge($contents1, $contents2);
        } else{
            define('IS_ENCODE', 0); // 设置状态为不加密
            $contents = $request->all();
        }

        // 设置一些默认的数据
        $this->siteParam($contents); // 获取站点常用参数
        //$this->myInfo();
        //$this->getSetting(); // 获取站点配置信息

        // TODO 页面token(闪存) 1、表单提交 2、接口返回
        // 表单token 所有的页面都有验证
        // $this->pageToken(true);
        /*if($request->ip() == '127.0.0.1'){
            $this->formToken(false);
        } else{
            $this->formToken(true);
        }*/


        // 控制器初始化
        $this->initialize();
    }


    // 初始化
    protected function initialize()
    {
    }

    // 关键字查询
    // $where[] = $this->orWhere('title|describe', 'like', $keyword);
    protected function orWhere($column, $type, $value)
    {
        // 失败 同where
        //$query->orWhere([['title', 'like', "%$keyword%"], ['describe', 'like', "%$keyword%"]]);
        // 成功
        //$query->orWhere('title', 'like', "%$keyword%")->orWhere('describe', 'like', "%$keyword%");

        $where = function($query) use ($column, $type, $value) {
            $columns = explode('|', $column);
            if($type == 'like'){
                $value = '%'.$value.'%';
            }
            foreach($columns as $_column){
                $query = $query->orWhere($_column, $type, $value);
            }
        };

        return [$where];
    }

    // 站点常用参数
    protected function siteParam($contents)
    {
        /**
         * 实体 => 符号
         * htmlspecialchars 中文不会乱码（获取的时候过滤，取出数据库的时候也要 htmlspecialchars_decode 过滤）
         * htmlentities  必须制定编码，不然中文乱码
         *
         * 反函数
         * html_entity_decode
         * htmlspecialchars_decode
         */
        // 循环过滤参数
        // 不操作的字段
        /*if(!empty($contents)){
            foreach($contents as $key => $value){
                if(is_string($value)){
                    $value = htmlspecialchars($value, ENT_NOQUOTES, "utf-8"); //把字符转换为 HTML 实体  htmlspecialchars
                    $xss   = new XssHtml($value);
                    $text  = $xss->getHtml();
                    $value = trim($text);
                }
                // 重新组合
                $contents[$key] = $value;
            }
        }*/


        // 由于第一个参数是 api 其他后面的几个参数就往后移
        $this->data = $contents;
        //$this->module     = $this->getData('module', 'frontend', '');
        //$this->controller = $this->getData('controller', 'index', '');
        //$this->action     = $this->getData('action', 'index', '');

        // 控制器
        $parameters       = $this->request->route()->parameters;
        $this->controller = $contents['controller'] ?? ($parameters['controller'] ?? '');
        $this->action     = $contents['action'] ?? ($parameters['action'] ?? '');
        // 控制器和方法可能是自定义的
        $this->request->offsetUnset('controller');
        $this->request->offsetUnset('action');

        //$this->is_debug = Env::get('app_debug');
        $this->isPage = empty($this->getData('page')) ? true : false;
        $this->page   = $this->getData('page', 1, 'intval');
        $this->rows   = $this->getData('rows', 10, 'intval');
        $this->id     = $this->getData('id', 0, 'intval');
        if($this->page < 1){
            $this->page = 1;
        }
        if($this->rows < 1 || $this->rows > 1000){
            $this->rows = 15;
        }

        // 如果重新搜索 变为第一页
        /*if($this->request->isPost()){
            $this->page = 1;
        }*/
    }

    // 判断操作模型是否存在
    protected function checkModel($model = '')
    {
        $this->throwIf(empty($model), '请指定数据模型！！');
        // 模型 与 查询
        $model = 'App\Models\\'.ucwords($model);
        if(!class_exists($model)){
            $request = request();
            $real_ip = $request->getClientIp();
            $test_ip = env('TEST_IP');
            $this->throwIf(true, '模型不存在！！');

            //$this->throwIf($real_ip == $test_ip, '模型不存在！！');

            return false;
        }

        return new $model;
    }

    // 过滤为空的字段
    protected function filterNull($data)
    {
        foreach($data as $key => $val){
            if(is_null($val)){
                unset($data[$key]);
            }
        }

        return $data;
    }

    // 七牛云图片大小调节
    protected function showQiniuImage($image, $width = 0, $height = 0)
    {
        if(empty($image) || empty($width)){
            return '';
        }

        // 如果已经处理了 则不在处理
        if(strpos($image, '?imageMogr2/auto-orient/thumbnail/') !== false){
            return $image;
        }

        // ?imageMogr2/auto-orient/thumbnail/100x65
        // ?imageMogr2/auto-orient/thumbnail/690
        // ?imageMogr2/quality/70/thumbnail/500
        $size = $width.($height ? 'x'.$height : '');
        $url  = $image.'?imageMogr2/auto-orient/thumbnail/'.$size;

        return $url;
    }

    ##########################################################################################
    ## 返回数据（这里最好转到控制器操作）


    // 整理结果（App中需要判断登录用户是否管理，后台不需要，所以不放在主控制器中）
    // $reAte 取消阿特
    protected function makeFreshListResult($lists, $fangtan_id = 0, $reAte = false)
    {
        if(empty($lists)){
            return $lists;
        }

        // 模型
        $fuDb      = new FreshUser();
        $fvDb      = new FreshVoice();
        $collectDb = new ArticleCollect();
        $nDescDb   = new NewsDesc();
        $needDb    = new Need();
        $eventDb   = new Event();
        $newsDb    = new News();
        $ftDb      = new Fangtan();
        $dispDb    = new Dispatch();
        $acDb      = new ArticleComment();
        $userDb    = new User();
        $freshDb   = new Fresh();
        $ufield    = $this->userField();

        // 访谈
        $fids    = array_column($lists, 'fangtan_id');
        $fids    = array_diff($fids, [0]);
        $ftlists = $ftDb->isCache(true)->getListInIds($fids);

        // 快讯
        $dids   = array_column($lists, 'dispatch_id');
        $dids   = array_diff($dids, [0]);
        $dlists = $dispDb->isCache(true)->getListInIds($dids);

        // 新闻
        $nids  = array_column($lists, 'news_id');
        $nids  = array_diff($nids, [0]);
        $news  = $newsDb->isCache(true)->getListInIds($nids);
        $descs = $nDescDb->isCache(true)->getListInIds($nids, '*', 'news_id');

        // 需求
        $edids = array_column($lists, 'need_id');
        $edids = array_diff($edids, [0]);
        $needs = $needDb->isCache(true)->getListInIds($edids);

        // 活动
        $evids  = array_column($lists, 'event_id');
        $evids  = array_diff($evids, [0]);
        $events = $eventDb->isCache(true)->getListInIds($evids);

        // 父级
        $pids   = array_column($lists, 'parent_id');
        $pids   = array_diff($pids, [0]);
        $plists = $freshDb->isCache(true)->getListInIds($pids);

        // 用户
        $uids  = array_column($lists, 'uid');
        $users = $userDb->isCache(true)->getListInIds($uids, $ufield);
        $types = $this->userType(); // 认证类型

        // 收藏长短文，收听的用户
        $flist  = $collectDb->getCollect($this->myUid, 'fresh');
        $flist  = array_column($flist, null, 'article_id');
        $inUids = $this->getShouting($this->myUid);

        // 长短文的点赞列表
        $bList = (new ArticleBehavior())->getBehavior($this->myUid, 'fresh', 'praise');
        $bList = array_column($bList, null, 'article_id');

        // 判断是否是App请求
        $is_app = strpos($this->request->getRequestUri(), '/mobile') === 0 ? true : false;

        // 如果是访谈  获取发布长短文用户在访谈中的身份
        if($fangtan_id){
            $fUserDb   = new FangtanUser();
            $fusers    = $fUserDb->isCache(true)->getList(['fangtan_id' => $fangtan_id, 'status' => 1]);
            $fusers    = is_object($fusers) ? $fusers->toArray() : $fusers;
            $userTypes = ['guest' => '嘉宾', 'host' => '主持人',];
            $fmember   = [];
            foreach($fusers as $_user){
                $_uid = $_user['uid'];
                //组合
                if($_user['itype'] == 'host'){
                    $fmember[$_uid] = $_user;
                } elseif($_user['itype'] == 'guest'){
                    $fmember[$_uid] = $_user;
                }
            }
        }

        // 整理
        foreach($lists as $key => $item){
            $_did     = $item['dispatch_id'];
            $_nid     = $item['news_id'];
            $_pid     = $item['parent_id'];
            $_enid    = $item['need_id'];
            $_evid    = $item['event_id'];
            $_ftid    = $item['fangtan_id'];
            $_uid     = $item['uid'];
            $_freshId = $item['id'];

            // 父级
            if($is_app){
                $parent = (object)[];
            } else{
                $parent = [];
            }

            // 如果是转发的 获取原文
            $prow = $plists[$_pid] ?? [];
            if($prow){
                $prow['channel'] = 'fresh';
                //$prow["images"]  = $this->makeFullFile($prow['images'], 690);  // 图片;
                $prow["images"] = $this->getThumbFiles($prow['images']);  // 图片;
                $prow['user']   = $this->getUserInfo($prow['uid']);
                if(!empty($prow["content"])){
                    // 添加插入用户（如果是访谈里面则不需要插入）
                    $prow["content"] = $this->makeResult4Content($prow["content"], $_pid);
                    $prow["content"] = $this->makeListShow($prow["content"], 'fresh');
                    if(empty($fangtan_id)){
                        //$pre_content     = "<text style='color: #2073FC;text-decoration:none;' data-id='".$prow['uid']."' data-type='2' class='test'>@".$prow['user']['username']."：</text>";
                        //$pre_content = $this->addAte($prow['user']);
                        $pre_content = '';
                        // 固定替换  style="text-align: left;"
                        $prow["content"] = str_replace(' style="text-align: left;"', "", $prow["content"]);
                        $prow["content"] = "<p>".$pre_content.mb_substr($prow["content"], 3);
                    }
                }
                // 父级
                $parent = $prow;
            }
            // 如果是文章
            $nrow = $news[$_nid] ?? [];
            if($nrow){
                $nrow['channel'] = 'news';
                $nrow['user']    = $this->getUserInfo($nrow['uid']);
                //$nrow["images"]  = $this->makeFullFile($nrow['images'], 690);  // 图片;
                $nrow["images"] = $this->getThumbFiles($nrow['images']);  // 图片;

                // 添加插入用户
                //$pre_content = "<text style='color: #2073FC;text-decoration:none;' data-id='".$nrow['uid']."' data-type='2' class='test'>@".$nrow['user']['username']."：</text>";
                //$pre_content = $this->addAte($nrow['user']);
                $pre_content = '';
                if(empty($nrow["description"])){
                    $desc_coontent = $descs[$_nid]["content"] ?? '';
                    if($desc_coontent){
                        $nrow_content = $this->makeListShow($desc_coontent, 'news');
                        //$nrow_content    = "<p>".$pre_content.mb_substr($nrow_content, 3);
                        $nrow["content"] = $this->makeResult4Content($nrow_content, 0);
                    } else{
                        $nrow["content"] = "";
                    }
                } else{
                    // description 描述 没有p标签（但不知道需要不需要组合p标签）
                    //$nrow["content"] = "<p>".$pre_content.mb_substr($nrow["description"], 3);
                    $nrow["content"] = $pre_content.$nrow["description"];
                }
                // 父级
                $parent = $nrow;
            }
            // 如果是需求
            $enrow = $needs[$_enid] ?? [];
            if($enrow){
                $enrow['channel'] = 'need';
                $enrow['user']    = $this->getUserInfo($enrow['uid']);
                $enrow["content"] = $this->makeListShow($enrow["content"], 'news');
                $enrow["images"]  = $this->getThumbFiles($enrow['images']);  // 图片;
                //$enrow["images"]  = $this->makeFullFile($enrow['images'], 690);  // 图片;
                // 父级
                $parent = $enrow;
            }
            // 如果是活动
            $evrow = $events[$_evid] ?? [];
            if($evrow){
                $evrow['channel'] = 'event';
                $evrow['user']    = $this->getUserInfo($evrow['uid']);
                $evrow["content"] = $this->makeListShow($evrow["content"], 'news');
                //$evrow["images"]  = $this->getThumbFiles($evrow['images']);  // 图片;
                //$evrow["images"]  = $this->makeFullFile($evrow['images'], 690);  // 图片;
                // 父级
                $parent = $evrow;
            }
            // 快讯
            $drow = $dlists[$_did] ?? [];
            if($drow){
                // 添加插入用户
                $drow['user'] = $this->getUserInfo($drow['uid']);
                //$pre_content  = $this->addAte($drow['user']);
                $pre_content = '';
                //$pre_content  = "<text style='color: #2073FC;text-decoration:none;' data-id='".$drow['uid']."' data-type='2' class='test'>@".$drow['user']['username']."：</text>";
                // 快讯没有p标签（但不知道需要不需要组合p标签）
                //$drow["content"] = "<p>".$pre_content.$drow["content"]."</p>";
                $drow["content"] = $pre_content.$drow["content"];
                $drow['channel'] = 'dispatch';
                $drow["images"]  = [];  // 图片;
                // 父级
                $parent = $drow;
            }
            // 访谈
            /*$ftrow = $ftlists[$_ftid] ?? [];
            if($ftrow){
                // 添加插入用户
                // $pre_content = "<text style='color: #2073FC;text-decoration:none;' data-id='".$ftrow['uid']."' data-type='2' class='test'>@".$ftrow['user']['username']."：</text>";
                // 快讯没有p标签（但不知道需要不需要组合p标签）
                //$drow["content"] = "<p>".$pre_content.$drow["description"]."</p>";
                //$drow['user']    = $this->getUserInfo($drow['uid']);

                // 访谈图片
                $cover = $this->getFullUrl($ftrow['cover']);

                $ftrow["content"] = $ftrow["description"];
                $ftrow['channel'] = 'fangtan';
                $ftrow["images"]  = [$cover];  // 图片;
                $ftrow['user']    = (object)[];
                // 父级
                $parent = $ftrow;
            }*/

            // 获取一条评论
            $where   = ['channel' => 'fresh', 'verify' => '1', 'pid' => '0', 'article_id' => $_freshId];
            $comment = $acDb->isCache(true)->getList($where, 1);
            $comment = is_object($comment) ? $comment->toArray() : $comment;
            if($comment){
                $comment = $comment[0];
                // 组合用户信息
                $comment['user']    = $this->getUserInfo($comment['uid']);
                $comment['content'] = $this->makeResult4Content($comment['content']);
            }

            // 是否点赞
            $brow = $bList[$_freshId] ?? [];
            // 是否收藏
            $frow = $flist[$_freshId] ?? [];
            // 用户信息
            $urow = $users[$_uid] ?? [];
            //$urow = $this->getUserInfo($_uid);
            if($urow){
                $urow["head_img"]   = $this->getHeadImg($urow["head_img"]);
                $urow['is_follow']  = in_array($urow['id'], $inUids) ? true : false;
                if(($urow['is_apply'] == 1) && ($urow['verify'] == 1)){
                    $urow['apply_type']  = $types[$urow['apply_type1']]['name'] ?? '';
                    $urow['apply_type2'] = $types[$urow['apply_type2']]['name'] ?? '';
                } else{
                    $urow['apply_type']  = '';
                    $urow['apply_type2'] = '';
                }
                if(!empty($fmember[$_uid])){
                    $urow['member_type'] = $userTypes[$fmember[$_uid]['itype']] ?? '';
                } else{
                    $urow['member_type'] = '';
                }
            }

            // 整理内容(列表需要去除图片)
            // 如果不是语音（number为语音长度，number不为0则为语音）
            if(empty($item["number"])){
                $content = $this->makeResult4Content($item["content"], $_freshId);
                $content = $this->makeListShow($content, 'fresh');
            } else{
                $content = $item["content"];
            }
            // 如果是访谈 提取阿特的人
            if($fangtan_id){
                // 新鲜事的阿特的人（$fuDb新鲜事用户表中存的是阿特的用户）
                $ftUser = $fuDb->bangField(['fresh_id'])->getList(['fresh_id' => $item["id"]]);
                $ftUser = is_object($ftUser) ? $ftUser->toArray() : $ftUser;
                $ftUids = array_column($ftUser, 'uid');
                // 阿特的用户
                $ateUsers = $userDb->isCache(true)->getListInIds($ftUids, $ufield);
                // 取消内容的阿特
                //if($this->action == 'answer'){
                if($reAte){
                    //过滤 @阿特的人 标签
                    /*$content = preg_replace("/<a.*?href=.*?id=(\d.*?)\&amp.*?>\@(.*?)<\/a>\s/is", "", $content);*/
                    $content = preg_replace("/<a.*?href=.*?id=(\d.*?)\&amp.*?>\@(.*?)<\/a>\s/is", "", $content);
                    /*$content = preg_replace("/<a.*?href=.*?id=(\d.*?)\&amp.*?>\@(.*?)<\/a>/is", "", $content);*/
                }
            }

            // 向某些人发送消息
            if($item['to_uid']){
                $to_users = [];
                $to_uids  = json_decode($item['to_uid']);
                foreach($to_uids as $_uid){
                    $to_users[] = $this->getUserInfo($_uid);
                }

                $lists[$key]['to_uid']  = $to_uids;
                $lists[$key]['to_user'] = $to_users;
            }

            // 如果是语音
            if($item['number'] > 0){
                $lists[$key]['voice'] = $fvDb->isCache(true)->getOneById($_freshId, ['*'], 'fresh_id');
            }

            // 组合一下
            $lists[$key]['is_favorite'] = $frow ? true : false;
            $lists[$key]['is_praise']   = empty($brow['status']) ? false : true;
            $lists[$key]['user']        = $urow;
            //$lists[$key]['images']      = $this->makeFullFile($item['images'], 690);  // 图片
            $lists[$key]['images']   = $this->getThumbFiles($item['images']);  // 图片
            $lists[$key]['comment']  = $comment ? $comment : (object)[];
            $lists[$key]['ate_user'] = $ateUsers ?? [];  // 阿特的用户
            //$lists[$key]['parent']      = $prow;
            $lists[$key]['channel'] = 'fresh';
            $lists[$key]['content'] = $content;
            $lists[$key]['parent']  = $parent;
            if($this->myUid){
                $lists[$key]['is_mime'] = $item['uid'] == $this->myUid ? 1 : 0;
            } else{
                $lists[$key]['is_mime'] = 0;
            }
        }

        return $lists;
    }


    // 获取我关注的用户id
    protected function getShouting($myUid, $apply_type = '')
    {
        $inUids = (new UserShouting())->getShoutingUid($myUid, $apply_type);

        return $inUids;
    }

    // 直接输出返回数据
    // 与output区别：output输出，前端多一个字段，用于判断返回的参数是否加密
    protected function message($message, $code = SUCCESS_CODE, $data = [])
    {
        $data = empty($data) ? [] : $data;
        if(IS_ENCODE){
            $data = $this->encode($data);
        }
        $json = [
            'msg'         => $message,
            'code'        => $code,
            'data'        => $data,
            // 重新设置页面token
            '_page_token' => $this->_page_token,
        ];
        $this->json($json);
    }

    // 接口处理成功时返回
    protected function success($data = [], string $message = '操作成功')
    {
        $data = empty($data) ? [] : $data;
        if(IS_ENCODE){
            $data = $this->encode($data);
        }
        $json = [
            'msg'         => $message,
            'code'        => SUCCESS_CODE,
            'data'        => $data,
            // 重新设置页面token
            '_page_token' => $this->_page_token,
        ];

        return $this->json($json);
    }

    // 接口处理失败时调用
    public function error(string $message = '操作失败', array $data = [], int $code = ERROR_CODE)
    {
        $data = empty($data) ? [] : $data;
        if(IS_ENCODE){
            $data = $this->encode($data);
        }
        $json = [
            'msg'         => $message,
            'code'        => $code,
            'data'        => $data,
            // 重新设置页面token
            '_page_token' => $this->_page_token,
        ];

        return $this->json($json);
    }

    // 输出JSON数据
    protected function json($data, $is_encode = IS_ENCODE)
    {
        $host_name = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '*';
        $options   = [];
        $header    = [
            'Access-Control-Allow-Origin'      => $host_name,
            'Access-Control-Allow-Credentials' => 'true',
            'Access-Control-Allow-Headers'     => 'Origin,X-Requested-With,Content-Type,Accept,Authorization',
            'Access-Control-Allow-Methods'     => 'POST,GET,OPTIONS',
        ];
        if($is_encode){
            //$data = ['data' => $this->encode($data)];
        } else{
            $data['access'] = 1; //不加密
        }

        return response()->json($data);
        //return Response::create($data, 'json', 200)->header($header)->options($options);
    }

    // 转化json,中文不转化
    protected function jsonChinese(array $data = [])
    {
        return json_encode($data, JSON_UNESCAPED_UNICODE);
    }
    #######################################################################################################
    ## 异常

    // 抛出异常
    protected function throwReturn($condition, $msg, $code = 400)
    {
        if($condition){
            throw new \RuntimeException($msg, $code);
        }
    }


    // 抛出异常
    protected function throwIf($condition, $msg, $code = 400)
    {
        if($condition){
            throw new \RuntimeException($msg, $code);
        }
    }

    // 在初始化中，不会抛出异常，只能使用终止程序
    protected function dieIf($condition, $msg, $code = 400, $is_encode = IS_ENCODE)
    {
        if($condition){
            $json = ['msg' => $msg, 'code' => $code, 'data' => [],];
            if($is_encode){
                //$json = ['data' => $this->encode($data)];
            } else{
                $json['access'] = 1; //不加密
            }

            $json = json_encode($json);
            die($json);
        }
    }

    // 必须登录
    protected function mustLogin($is_die = false)
    {
        if($is_die){
            $this->dieIf(empty($this->my_user), '请先登录，在操作！', LOGIN_ERROR);
        } else{
            $this->throwReturn(empty($this->my_user), '请先登录，在操作！', LOGIN_ERROR);
        }
    }
    #######################################################################################################
    ## 其他操作
    // 取得数据

    // TODO 深蓝汇开始验证用户提交数据类型
    // $func 数据转换函数  如果为 true 则为 null 的时候也返回默认值
    protected function getData($name = '', $def = null, $func = '')
    {
        $data = $this->data;
        if(empty($name)){
            return $data;
        }

        if(!isset($data[$name])){
            return $def;
        }
        /*if($data[$name]){
            if(empty($def)){
                return $data[$name];
            } else{
                return $def;
            }
        }*/
        // 传递为空字符串 接收为 null
        if(is_null($data[$name])){
            return $def;
        }

        // 特殊字段
        if(($name == 'page') && (!empty($this->page))){
            return $this->page;
        } elseif(($name == 'rows') && (!empty($this->rows))){
            return $this->rows;
        }

        // 已经经过安全过滤操作 （过滤xss）
        $value = $data[$name];
        if(empty($func)){
            return $value;
        }

        // php 7种数据类型
        // 一般不是数组或对象的操作
        // 默认操作方法是  trim
        $return = null;
        if(in_array($func, ['trim', 'intval'])){
            $this->throwReturn(is_object($value) || is_array($value), '参数错误，'.$name.'不能是数组或对象！');

            // 如果是默认方法 trim 同时又是传递的是数字 则不做任何处理
            //$this->throwReturn(is_numeric($value) && $func=='trim','参数错误，'.$name.'必须是字符串！');
            if($func == 'trim' && is_numeric($value)){
                return $value;
            }
        }
        if(in_array($func, ['is_array', 'json_encode'])){
            $this->throwReturn(!is_array($value), '参数错误，'.$name.'必须是数组或对象');
            if($func == 'is_array'){
                return $value;
            } elseif($func == 'json_encode'){
                return $this->jsonChinese($value);
            }
        }

        // 如果返回的没有处理 重新处理一下
        if(is_null($return)){
            $return = $func($value);
        }

        return $return;
    }

    // 获取Header中的参数值
    protected function getHeader(string $key = '', $default = null)
    {
        // HTTP请求头信息的名称不区分大小写，并且 _ 会自动转换为 -
        $result = $this->request->header($key);
        if(empty($result)) return $default;

        return $result;
    }


    // 取得数据（过滤不需要的数据，避免保存到数据的时候污染其他字段的数据）
    protected function filterData(array $fields)
    {
        $result = [];
        foreach($fields as $_field){
            $value = $this->getData($_field);
            if(is_null($value)){
                continue;
            }

            $result[$_field] = $value;
        }

        return $result;
    }


    // 自己写的图片压缩文件方法（修改图片质量）
    protected function toCompress($file, $toFile)
    {
        // 图片信息
        $splFile  = new \SplFileInfo($file);
        $filePath = $splFile->getPathname();
        //获取图像信息
        $info = getimagesize($filePath);
        $type = image_type_to_extension($info[2], false);
        $fun  = "imagecreatefrom{$type}";
        $im   = @$fun($filePath);

        // 新图片高与宽
        $width     = $info[0];
        $height    = $info[1];
        $to_width  = 500;
        $to_height = $height * $to_width / $width;

        // 制作等比例图片
        if($width > $to_width){
            //创建新图像
            $x   = $y = 0;
            $img = imagecreatetruecolor($to_width, $to_height);
            imagesavealpha($img, true); //这里很重要 意思是不要丢了$sourePic图像的透明色
            $color = imagecolorallocatealpha($img, 255, 255, 255, 127);
            imagefill($img, 0, 0, $color);
            // 裁剪
            imagecopyresampled($img, $im, 0, 0, $x, $y, $to_width, $to_height, $width, $height);
            imagedestroy($im); //销毁原图
            // 新突破
            $im = $img;
        }

        // 保存图片
        imageinterlace($im, 1);  // 间隔扫描 1-提升质量
        imagejpeg($im, $toFile, 70);  // 图片质量70
        imagedestroy($im);

        return $toFile;
    }

    ###############################################################################################
    ## 控制器中要用到的方法
    ###############################################################################################
    // 从内容中提取图片
    protected function getImage4content($content)
    {
        //preg_match_all('/<[\s]*img.*?src=[\'|"|\s]*(.+?|\ )[\'|"|\s|>]/is', $content, $matches);
        preg_match_all('/<[\s]*[img|IMG].*?src=[\'|"|\s]*(.+?|\ )[\'|"|\s|>]/is', $content, $matches);
        $aryimg = array();
        if($matches[1]){
            $domain = Config::get('site.cdnHost');
            $domain = substr($domain, -1) == '/' ? substr($domain, 0, -1) : $domain;
            $long   = strlen($domain);
            for($j = 0; $j < count($matches[1]); $j++){
                // 判断是后缀，如果是表情  则不添加
                $_url = $matches[1][$j];
                $ext  = pathinfo($_url, PATHINFO_EXTENSION);
                $_url = str_replace('&amp;', '&', $_url);  // 避免获取到的图片不能展示
                if($ext == 'gif'){
                    continue;
                }

                if(substr($_url, 0, $long - 1) == $domain){
                    $_url = substr($_url, $long);
                }

                $aryimg[] = $_url;
            }
        }

        return $aryimg; //$matches[1][$j][0] //返回第一个匹配的图
    }

    // 文件地址
    protected function getFullUrl($url)
    {
        if(empty($url) || is_array($url) || is_object($url)){
            return $url;
        }
        if(substr($url, 0, 4) != 'http'){
            $domain = Config::get('site.cdnHost');
            $domain = substr($domain, -1) == '/' ? substr($domain, 0, -1) : $domain;
            $url    = $domain.$url;
        }

        return $url;
    }

    // 获取默认头像
    protected function getHeadImg($url)
    {
        if(empty($url)){
            $url = '/static/no_head_img.jpg';
        }
        if(substr($url, 0, 4) != 'http'){
            $domain = Config::get('site.cdnHost');
            $domain = substr($domain, -1) == '/' ? substr($domain, 0, -1) : $domain;
            $url    = $domain.$url;
        }

        return $url;
    }

    // 文件全域名地址
    protected function makeFullFile($images, $width = 0, $height = 0)
    {
        $allImg = is_array($images) ? $images : json_decode($images);
        $images = [];
        if($allImg){
            foreach($allImg as $key => $_img){
                // 页面只显示3张图片  showQiniuImage
                /*if($key >= 4){
                    break;
                }*/
                $_img = $this->getFullUrl($_img);
                if($width){
                    $_img = $this->showQiniuImage($_img, $width, $height);
                }

                // 整理
                $images[] = $_img;
            }
        }

        return $images;
    }

    // 文件全域名地址的缩略图
    protected function getThumbFiles($images)
    {
        $allImg = is_array($images) ? $images : json_decode($images);
        $images = [];
        if($allImg){
            foreach($allImg as $key => $_img){
                // 页面只显示3张图片  showQiniuImage
                /*if($key >= 4){
                    break;
                }*/
                $_img = $this->getFullUrl($_img);

                // 整理
                $images[] = $_img.'_thumb.jpg';
            }
        }

        return $images;
    }

    // 获取文档中的阿特的人与话题
    protected function getData4Fresh($content, $is_draft = 0)
    {
        $uids = $topids = $show = $title = [];
        if(empty($is_draft)){
            preg_match_all('/<a.*?href=.*?id=(\d.*?)\&amp.*?>(.*?)<\/a>/is', $content, $matches);
            if($matches[1]){
                foreach($matches[1] as $key => $id){
                    $_name = $matches[2][$key];
                    $_type = '';
                    if(substr($_name, 0, 1) == '#'){
                        $topids[] = $id;
                        $_type    = '1';
                    } elseif(substr($_name, 0, 1) == '@'){
                        $uids[] = $id;
                        $_type  = '2';
                    }

                    $show[$key]  = [$id => $_type];
                    $title[$key] = [$id => $_name];
                }
            }
        }

        return ['uids' => $uids, 'topids' => $topids, 'show' => $show, 'title' => $title];
    }


    ## 查找
    protected function getUserInfo($uid)
    {
        $action = 'controller';  // 指定为控制器，避免有缓存数据重复覆盖
        $userDb = new User();
        $field  = $this->userField();
        $field  = '*';
        $where  = ['id' => $uid];
        $args   = compact('where', 'field', 'action');
        // 获取缓存
        $cacheName = $userDb->cacheKey('show', $args);
        $info      = $userDb->cacheClass()->getCache($cacheName);
        if(empty($info)){
            // 重新查询 并缓存
            $info = $userDb->where($where)->select($field)->first();
            $info = is_object($info) ? $info->toArray() : $info;
            // 整理头像
            if($info){
                $types = $this->userType();
                // 认证类型
                //$info['apply_type'] = (($info['is_apply'] == 1) && ($info['verify'] == 1)) ? ($types[$info['apply_type']]['name'] ?? '') : '';
                $info["head_img"]   = $this->getHeadImg($info["head_img"]);
                if(($info['is_apply'] == 1) && ($info['verify'] == 1)){
                    $info['apply_type']  = $types[$info['apply_type1']]['name'] ?? '';
                    $info['apply_type2'] = $types[$info['apply_type2']]['name'] ?? '';
                } else{
                    $info['apply_type']  = '';
                    $info['apply_type2'] = '';
                }
            }
            $userDb->addCacheShow($info, $cacheName, $info);
        }
        if(empty($info)){
            $info = (object)[];
        }

        // 获取用户二维码图片
        $info['qrcode_url'] = (new \App\Service\QRcode())->getQR($uid);

        return $info;
    }

    // 执行百度审核：文字审核
    public function baiduTextCensor($string)
    {
        $to = '';
        if(is_array($string)){
            /*foreach($string as $str){
                $to .= $str;
            }*/
            $string = var_export($string, 1);
        }
        // 执行审核操作
        (new BaiduCensor())->text($string);
    }

    // 执行百度审核：图片审核
    public function baiduImageCensor($file)
    {
        // 执行审核操作
        (new BaiduCensor())->image($file);
    }

    // 执行百度审核：语音审核
    public function baiduVoiceCensor($file)
    {
        // 执行审核操作
        return (new BaiduCensor())->voice($file);
    }

    // 发送百度短信
    public function baiduSms($mobile, $temp, $send)
    {
        $signatureId = 'sms-sign-xLCZQQ25974'; // 签名
        $config      = ['endPoint' => 'smsv3.bj.baidubce.com', 'accessKey' => 'ALTAKdUwyVdZbGim9i7WX6W5DE', 'secretAccessKey' => 'bd3d5f3dcbd7492f8e308e053e0be1a9',];
        $post        = ['mobile' => $mobile, 'template' => $temp, 'signatureId' => $signatureId, 'contentVar' => $send,];
        $sms         = new BaiduSms($config);
        //$res = BaiduSms::sendSms($to);//发送
        $res = $sms->sendSms($post);//发送

        return $res;
    }

    // 根据ip获取地址
    public function getIpAddress($ip)
    {
        return (new Ip())->getIpAddress($ip);
    }

    public function getIpAddress2($ip)
    {
        // 首先判断ip是否已经查询了
        $ipDb = new SiteIp();
        $info = $ipDb->isCache(true)->oneFind(['ip' => $ip]);
        if(!empty($info)){
            return $info['city'];
        }

        // 远程获取ip的地址
        // https://api.ipplus360.com/ip/geo/v1/street/psi/?key=您申请的KEY&ip=您需要查询的ip&coordsys=WGS84&area=multi
        // https://api.ipplus360.com/ip/geo/v1/street/psi/?key=EbwJgRSFafs4jaGOAFuyVIr86ZrpRh2EwOLB3h0nASb3kzxAwCnc4QN26d7qgrjm&ip=您需要查询的ip&coordsys=WGS84&area=multi
        $appkey = 'EbwJgRSFafs4jaGOAFuyVIr86ZrpRh2EwOLB3h0nASb3kzxAwCnc4QN26d7qgrjm';
        $url    = "https://api.ipplus360.com/ip/geo/v1/street/psi/?key={$appkey}&ip={$ip}&coordsys=WGS84&area=multi";
        $url    = "https://api.ipplus360.com/ip/geo/v1/city/";
        $data   = ['key' => $appkey, 'ip' => $ip, 'coordsys' => 'WGS84', 'area' => 'multi',];

        try{
            // 初始化curl
            $curl = curl_init();
            curl_setopt($curl, CURLOPT_URL, $url);
            curl_setopt($curl, CURLOPT_HEADER, 0);
            // 要求结果为字符串且输出到屏幕上
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
            // post提交方式
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
            // 运行curl
            $data = curl_exec($curl);
            curl_close($curl);

            // 返回ip的地址
            $data = json_decode($data, 1);
            if($data['code'] == 'Success'){
                // 保存到数据库
                $data = $data['data'];
                $add  = ['city' => $data['city'], 'prov' => $data['prov'], 'ip' => $ip,];
                $res  = $ipDb->insert($add);

                return $data['city'];
            } else{
                return '';
            }
        } catch(\Exception $e){
            echo $ipDb->getError();

            return '';
        }

    }

    ###############################################################################################
    ## 结果处理
    ###############################################################################################

    // 整理内容（改变内容中的阿特与话题）
    // 如果有
    protected function makeResult4Content($content, $fresh_id = 0)
    {
        $content   = str_replace('<p><br></p>', '', $content);
        $freshData = $this->getData4Fresh($content, 0);
        // 如果有新鲜事，则充数据表中获取阿特的人和话题
        if($fresh_id){
            // 新鲜事的话题
            $tList    = (new FreshTopic())->bangField(['fresh_id'])->getList(['fresh_id' => $fresh_id]);
            $tList    = is_object($tList) ? $tList->toArray() : $tList;
            $topicIds = array_column($tList, 'topic_id');
            // 新鲜事的阿特的人
            $uList = (new FreshUser())->bangField(['fresh_id'])->getList(['fresh_id' => $fresh_id]);
            $uList = is_object($uList) ? $uList->toArray() : $uList;
            $uids  = array_column($uList, 'uid');
        } else{
            $uids     = $freshData['uids'];
            $topicIds = $freshData['topids'];
        }
        // 话题
        $topics = (new Topic())->isCache(true)->getListInIds($topicIds);
        // 阿特的用户
        $users = (new User())->isCache(true)->getListInIds($uids);
        // 如果没有阿特的人 或 选择的话题 则直接返回数据
        if(empty($topics) && empty($users)){
            return $content;
        }

        // 采取内容
        $contents = explode('</a>', $content);
        $string   = '';
        foreach($contents as $key => $item){
            if(empty($freshData['show'][$key])){
                $string .= $item;
                break;
            }
            $arr_key = array_key_first($freshData['show'][$key]);
            $arr_val = $freshData['show'][$key][$arr_key];
            if($arr_val == 1){
                // 如果是 话题
                if(!empty($topics[$arr_key])){
                    $string .= substr($item, 0, strpos($item, '>#')).'>#'.$topics[$arr_key]['title'].'#</a>';
                } else{
                    $string .= $item.'</a>';
                }
            } elseif($arr_val == 2){
                // 如果是 阿特的人
                if(!empty($users[$arr_key])){
                    // 用户需要判断是否是认证用户
                    $_userInfo = $users[$arr_key];
                    $_realname = ($_userInfo['is_apply'] && $_userInfo['is_realname']) ? $_userInfo['realname'] : $_userInfo['username'];
                    $string    .= substr($item, 0, strpos($item, '>@')).'>@'.$_realname.'</a>';
                } else{
                    $string .= $item.'</a>';
                }
            } else{
                $string .= $item.'</a>';
            }
        }

        return $string;
    }

    // 获取用户信息的字段（用的地方比较多）
    protected function userField()
    {
        return ['id', 'username', 'realname', 'head_img', 'phone', 'email', 'itype', 'mobile', 'channel', 'sex', 'apply_type1', 'apply_type2', 'no_first', 'is_apply', 'is_realname', 'auth_way', 'verify', 'birthday'];
    }

    // 去除列表中的图片
    protected function makeListShow($str, $channel = 'fresh')
    {
        if(empty($str)){
            return $str;
        }
        // 新闻列表，去掉样式
        if($channel == 'news'){
            //$str = preg_replace("/style=\"(.*?)\"/si", "", $str); // 过滤样式
        }

        /*$str = preg_replace("/(<img.*?>)(.*?)(<\/img>)/si", "", $str); //过滤img标签*/
        //$str = preg_replace("/<img.*?(BMP|DIB|PCP|DIF|WMF|GIF|JPG|TIF|EPS|CDR|IFF|TGA|PCD|MPT|PNG|JPEG|PSD|TIFF|bnp).*?\/>/si", "", $str); //过滤img标签 ()

        /*$str = preg_replace("/<img.*?>/si", "", $str); //过滤img标签(前半部分)
        $str = preg_replace("/<\/img>/si", "", $str); //过滤img标签*/

        // 针对特定的内容过滤图片（因为表情包是gif的，所以这里不过滤表情包）
        // 有些网络图片是没有后缀的，所以这里过滤不成功
        $str = preg_replace("/<img.*?\.(BMP|DIB|PCP|DIF|WMF|GIF|JPG|TIF|EPS|CDR|IFF|TGA|PCD|MPT|PNG|JPEG|PSD|TIFF|bnp).*?>/si", "", $str); //过滤img标签(前半部分)
        $str = preg_replace("/<\/img>/si", "", $str); //过滤img标签(后台半部分)
        $str = str_replace("<p><br></p>", "", $str); //过滤 <p><br></p> 产生的空行
        //$str = str_replace("<p></p>", "", $str); //过滤 <p><br></p> 产生的空行

        // 针对特定的内容过滤图片（这种方法过滤图片会过滤表情包）
        /*$str = preg_replace("/<img.*?>/si", "", $str); //过滤img标签(前半部分)
        $str = preg_replace("/<\/img>/si", "", $str); //过滤img标签(后台半部分)*/

        return $str;
    }

    ###############################################################################################
    ## before 操作
    ###############################################################################################


    // 加密
    protected function encode($data)
    {
        $data = is_array($data) ? json_encode($data, JSON_UNESCAPED_UNICODE) : $data;

        // 加密配置
        $iv   = '';
        $type = 'DES-ECB';
        $type = 'AES-128-CBC';
        $conf = Config::get('encrypt');
        $key  = substr($conf['key'], 0, 24);
        if($type == 'AES-128-CBC' || $type == 'AES-192-CBC'){
            //$iv = '1234567812345678';
            $iv = substr($conf['iv'], 0, 16);
        }

        $str = openssl_encrypt($data, $type, $key, 0, $iv);

        return base64_encode($str);
    }

    // 解密
    protected function decode($str)
    {
        if(empty($str)){
            return '';
        }


        $string = base64_decode($str);

        // 加密配置
        $iv   = '';
        $type = 'DES-ECB';
        $type = 'AES-128-CBC';
        $conf = Config::get('encrypt');
        $key  = substr($conf['key'], 0, 24);
        if($type == 'AES-128-CBC' || $type == 'AES-192-CBC'){
            //$iv = '1234567812345678';
            $iv = substr($conf['iv'], 0, 16);
        }


        return openssl_decrypt($string, $type, $key, 0, $iv);
        //return openssl_decrypt($string, $type, $key, OPENSSL_RAW_DATA, $iv);
    }


    /**
     * 改写父级控制器中的 callAction 用于构建前置与后置操作方法【失败】
     */
    public function callAction($method, $parameters)
    {
        $before = '_before_'.$method;
        $after  = '_after_'.$method;
        // 前置操作
        if(method_exists($this, $before)){
            $this->{$before}(...array_values($parameters));
        }

        $result = $this->{$method}(...array_values($parameters));

        // 后置操作
        if(method_exists($this, $after)){
            $this->{$after}(...array_values($parameters));
        }


        return $result;
    }
}


